#region Copyright (c) 2013-06, Olaf Kober <amarok.projects@gmail.com>
//================================================================================
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//================================================================================
#endregion

using System;
using System.Threading.Tasks;
using Amarok.Agents;
using Amarok.Presentation.Resources;


namespace Amarok.Presentation
{
	/// <summary>
	/// This type represents a view description, which is a description of the combination of view, view model and 
	/// associated controller. In addition a view description is capable to instantiate and connect view and 
	/// controller and also to manage the lifetimes of those instances.
	/// </summary>
	public abstract class ViewDescription : CompletableObject,
		ICloneable
	{
		// data
		private IView mView;
		private IController mController;


		#region ++ ICompletable Interface ++

		/// <summary>
		/// Gets a Task instance that represents the completion state of the object. This task instance is 
		/// transitioned into the final RunToCompletion state when the object has finally completed.
		/// </summary>
		public new Task Completion
		{
			get
			{
				return base.Completion;
			}
		}


		/// <summary>
		/// Initiates the asynchronous completion of the object. After calling this method the object will complete 
		/// and its <see cref="Completion"/> task will enter a final state after it has processed or discarded all 
		/// previously available messages.
		/// </summary>
		/// 
		/// <param name="discardAvailableMessages">
		/// True to discard all already available messages. False to process all already available messages to 
		/// completion. Defaults to false.</param>
		public void Complete(Boolean discardAvailableMessages = false)
		{
			base.CompleteObject(discardAvailableMessages);
		}

		/// <summary>
		/// When calling <see cref="IDisposable.Dispose"/> all available messages will be discarded and the method 
		/// will block the calling thread until the object has finally completed.
		/// </summary>
		public void Dispose()
		{
			base.DisposeObject();
		}

		#endregion

		#region ++ ICloneable Interface ++

		/// <summary>
		/// Creates a new ViewDescription instance that is a copy of the current instance.
		/// </summary>
		/// 
		/// <returns>
		/// A new ViewDescription instance that is a copy of this instance.</returns>
		public ViewDescription Clone()
		{
			return (ViewDescription)Activator.CreateInstance(this.GetType());
		}

		/// <summary>
		/// Creates a new ViewDescription instance that is a copy of the current instance.
		/// </summary>
		/// 
		/// <returns>
		/// A new ViewDescription instance that is a copy of this instance.</returns>
		Object ICloneable.Clone()
		{
			return this.Clone();
		}

		#endregion

		#region ++ Public Interface ++

		/// <summary>
		/// Gets a boolean value indicating whether view and controller have been created.
		/// </summary>
		public Boolean IsCreated
		{
			get
			{
				return this.View != null || this.Controller != null;
			}
		}


		/// <summary>
		/// Gets the type of view in this combination of view, view model and controller.
		/// </summary>
		public Type ViewType
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the type of controller in this combination of view, view model and controller.
		/// </summary>
		public Type ControllerType
		{
			get;
			private set;
		}


		/// <summary>
		/// Gets a reference to the view instance. This property returns null, if Create() was not called before.
		/// </summary>
		public IView View
		{
			get
			{
				return mView;
			}
		}

		/// <summary>
		/// Gets a reference to the controller instance. This property returns null, if Create() was not called before.
		/// </summary>
		public IController Controller
		{
			get
			{
				return mController;
			}
		}


		/// <summary>
		/// Instantiates view, view model and controller and connects all together.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment for this controller.</param>
		/// <param name="options">
		/// The agent options for this controller.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		/// <exception cref="InvalidOperationException">
		/// The view description is already created.</exception>
		public void Create(AgentEnvironment environment, AgentOptions options)
		{
			Verify.NotNull(environment, "environment");
			Verify.NotNull(options, "options");

			if (this.IsCreated)
				throw new InvalidOperationException(ExceptionResources.ViewDescriptionAlreadyCreated);

			mView = CreateView(environment, options);
			mController = CreateController(environment, options);
		}

		/// <summary>
		/// Instantiates view, view model and controller and connects all together.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment for this controller.</param>
		/// <param name="options">
		/// The agent options for this controller.</param>
		/// <param name="viewModel">
		/// The view model that is used as intermediate between controller and view.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		/// <exception cref="InvalidOperationException">
		/// The view description is already created.</exception>
		public void Create(AgentEnvironment environment, AgentOptions options, Object viewModel)
		{
			Verify.NotNull(environment, "environment");
			Verify.NotNull(options, "options");

			if (this.IsCreated)
				throw new InvalidOperationException(ExceptionResources.ViewDescriptionAlreadyCreated);

			mView = CreateView(environment, options);
			mController = CreateController(environment, options, viewModel);
		}

		#endregion

		#region ## Protected Interface ##

		/// <summary>
		/// Initializes a new instance.
		/// </summary>
		/// 
		/// <param name="viewType">
		/// The type of view in this combination of view, view model and controller.</param>
		/// <param name="controllerType">
		/// The type of controller in this combination of view, view model and controller.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		/// <exception cref="ArgumentException">
		/// A value was passed to a method that did not accept it as a valid argument, because 
		/// the value representing a type must be a sub-class of specified base type.</exception>
		protected ViewDescription(Type viewType, Type controllerType)
		{
			Verify.IsAssignableTo(viewType, typeof(IView), "viewType");
			Verify.IsAssignableTo(controllerType, typeof(IController), "controllerType");

			this.ViewType = viewType;
			this.ControllerType = controllerType;
		}

		#endregion

		#region ## Overridable Methods ##

		/// <summary>
		/// This overrideable method is called to create the view instance.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment for this controller.</param>
		/// <param name="options">
		/// The agent options for this controller.</param>
		/// 
		/// <returns>
		/// A reference to the newly created view instance.</returns>
		protected abstract IView CreateView(AgentEnvironment environment, AgentOptions options);

		/// <summary>
		/// This overrideable method is called to create the controller instance.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment for this controller.</param>
		/// <param name="options">
		/// The agent options for this controller.</param>
		/// 
		/// <returns>
		/// A reference to the newly created controller instance.</returns>
		protected abstract IController CreateController(AgentEnvironment environment, AgentOptions options);

		/// <summary>
		/// This overrideable method is called to create the controller instance.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment for this controller.</param>
		/// <param name="options">
		/// The agent options for this controller.</param>
		/// <param name="viewModel">
		/// The view model that is used as intermediate between controller and view.</param>
		/// 
		/// <returns>
		/// A reference to the newly created controller instance.</returns>
		protected abstract IController CreateController(AgentEnvironment environment, AgentOptions options, Object viewModel);

		#endregion

		#region Implementation

		/// <summary>
		/// This method supports the agent infrastructure and is not intended to be used directly.
		/// </summary>
		protected override Task OnComplete(Boolean discardAvailableMessages)
		{
			if (mController != null)
			{
				mController.Complete(discardAvailableMessages);
				return mController.Completion;
			}
			else
			{
				return TaskHelper.Completed;
			}
		}

		#endregion

	}
}
